package org.fujene.communicate.indexer.term;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import org.fujene.toolbox.ByteConverter;
import org.msgpack.MessagePackObject;
import org.msgpack.object.ArrayType;
import org.msgpack.object.IntegerType;
import org.msgpack.object.RawType;

public class TermPackage{
	private String term;
	private long ID;
	private int termFreq;
	private int valueType;
	private int cluster;
	private boolean[] binaryFlags;
	private int[] multiFlags;

	private TermPackage(){}

	public static TermPackage valueOf(String term, long ID, int termFreq, int valueType, boolean[] binaryFlag,
		int[] multiFlag){
		TermPackage instance = new TermPackage();
		instance.term = term;
		instance.ID = ID;
		instance.termFreq = termFreq;
		instance.valueType = valueType;
		instance.binaryFlags = binaryFlag;
		instance.multiFlags = multiFlag;

		return instance;
	}

	public static boolean[] byteToFlag(byte[] b){
		if(b.length < 2) return null;
		boolean[] newset = new boolean[ByteConverter.byteToInt(b, 0, 2)];

		int counter = 0;
		int filter;
		for(int i = 2; i < b.length; i++){
			filter = 1;
			for(int j = 0; j < 8 && counter < newset.length; j++, counter++){
				if((b[i] & filter) != 0) newset[8 * (i - 2) + j] = true;
				else newset[8 * (i - 2) + j] = false;
				filter <<= 1;
			}
		}

		return newset;
	}

	public static byte[] flagToByte(boolean[] flags){
		int flagSize = flags.length;
		if(flagSize == 0) return new byte[2];
		byte[] flagArray;
		if(flagSize % 8 == 0) flagArray = new byte[flagSize / 8 + 2];
		else flagArray = new byte[flagSize / 8 + 3];

		int counter = 0;
		System.arraycopy(ByteConverter.intToByte(flagSize, 2), 0, flagArray, 0, 2);
		for(int i = 2; i < flagArray.length; i++){
			for(int j = 0; j < 8 && counter < flagSize; j++, counter++)
				flagArray[i] += ((flags[8 * (i - 2) + j]? 1: 0) << j);
		}
		return flagArray;
	}

	public static TermPackage fromByteArray(byte[] b){
		TermPackage pkg = new TermPackage();
		ByteArrayInputStream stream = new ByteArrayInputStream(b);

		try{
			byte[] readb = new byte[8];
			stream.read(readb);
			pkg.ID = ByteConverter.byteToLong(readb);

			readb = new byte[2];
			stream.read(readb);
			pkg.termFreq = ByteConverter.byteToInt(readb);

			stream.read(readb);
			pkg.valueType = ByteConverter.byteToInt(readb);

			stream.read(readb);
			pkg.cluster = ByteConverter.byteToInt(readb);

			int flaglen = stream.read();
			readb = new byte[flaglen];
			stream.read(readb);
			pkg.binaryFlags = byteToFlag(readb);

			flaglen = stream.read();
			pkg.multiFlags = new int[flaglen];

			readb = new byte[4];
			for(int i = 0; i < flaglen; i++){
				stream.read(readb);
				pkg.multiFlags[i] = ByteConverter.byteToInt(readb);
			}

			readb = new byte[2];
			stream.read(readb);
			int strlen = ByteConverter.byteToInt(readb);
			readb = new byte[strlen];
			stream.read(readb);
			pkg.term = new String(readb, Charset.forName("UTF-8"));
		}catch(IOException ignored){}

		return pkg;
	}

	public static TermPackage fromRemoteObject(ArrayType array){
		TermPackage pkg = new TermPackage();
		MessagePackObject[] objs = array.asArray();

		pkg.ID = objs[0].asLong();
		pkg.termFreq = objs[1].asInt();
		pkg.valueType = objs[2].asInt();
		pkg.cluster = objs[3].asInt();
		pkg.binaryFlags = byteToFlag(objs[4].asByteArray());
		pkg.multiFlags = new int[objs[5].asInt()];
		pkg.term = objs[6].asString();
		for(int i = 0; i < pkg.multiFlags.length; i++)
			pkg.multiFlags[i] = objs[7 + i].asInt();

		return pkg;
	}

	public byte[] toByteArray(){
		ByteArrayOutputStream stream = new ByteArrayOutputStream();

		try{
			stream.write(ByteConverter.longToByte(ID, 8));
			stream.write(ByteConverter.intToByte(termFreq, 2));
			stream.write(ByteConverter.intToByte(valueType, 2));
			stream.write(ByteConverter.intToByte(cluster, 2));
			byte[] flagb = flagToByte(binaryFlags);
			stream.write(flagb.length); // 1byte
			stream.write(flagb);
			stream.write(multiFlags.length); // 1byte
			for(int i = 0; i < multiFlags.length; i++){
				stream.write(ByteConverter.intToByte(multiFlags[i], 4));
			}
			byte[] strb = term.getBytes(Charset.forName("UTF-8"));
			stream.write(ByteConverter.intToByte(strb.length, 2));
			stream.write(strb);
		}catch(IOException ignored){}

		return stream.toByteArray();
	}

	public ArrayType getRemoteObject(){
		MessagePackObject[] objs = new MessagePackObject[7 + multiFlags.length];

		objs[0] = IntegerType.create(ID);
		objs[1] = IntegerType.create(termFreq);
		objs[2] = IntegerType.create(valueType);
		objs[3] = IntegerType.create(cluster);
		objs[4] = RawType.create(flagToByte(binaryFlags));
		objs[5] = IntegerType.create(multiFlags.length);
		objs[6] = RawType.create(term);
		for(int i = 0; i < multiFlags.length; i++)
			objs[7 + i] = IntegerType.create(multiFlags.length);

		return ArrayType.create(objs);
	}

	public void setCluster(int cluster){
		this.cluster = cluster;
	}

	public String term(){
		return term;
	}

	public long ID(){
		return ID;
	}

	public int termFreq(){
		return termFreq;
	}

	public int valueType(){
		return valueType;
	}

	public int cluster(){
		return cluster;
	}

	public boolean binaryFlag(int index){
		return binaryFlags[index];
	}

	public boolean[] binaryFlags(){
		return binaryFlags;
	}

	public int multiFlag(int index){
		return multiFlags[index];
	}

	public int[] multiFlags(){
		return multiFlags;
	}
}
